Skip to main content

libobs_wrapper\data\traits/
getters.rs

1use std::ffi::CStr;
2
3use crate::{
4    data::ObsDataPointers,
5    run_with_obs,
6    unsafe_send::SmartPointerSendable,
7    utils::{ObsError, ObsString},
8};
9
10/// # Safety
11/// This function must be called on the OBS runtime.
12#[allow(unknown_lints)]
13#[allow(ensure_obs_call_in_runtime)]
14unsafe fn has_value(
15    data_ptr: SmartPointerSendable<*mut libobs::obs_data_t>,
16    key: &ObsString,
17) -> bool {
18    libobs::obs_data_has_user_value(data_ptr.get_ptr(), key.as_ptr().0)
19        || libobs::obs_data_has_default_value(data_ptr.get_ptr(), key.as_ptr().0)
20}
21
22#[allow(private_bounds)]
23pub trait ObsDataGetters: ObsDataPointers {
24    fn get_string<T: Into<ObsString> + Send + Sync>(
25        &self,
26        key: T,
27    ) -> Result<Option<String>, ObsError> {
28        let key = key.into();
29        let data_ptr = self.as_ptr();
30
31        run_with_obs!(self.runtime(), (data_ptr, key), move || {
32            let has_value = unsafe {
33                // Safety: We are running on the OBS runtime.
34                has_value(data_ptr.clone(), &key)
35            };
36
37            if has_value {
38                let result = unsafe {
39                    // Safety: The pointer is valid because we are using a smart pointer
40                    libobs::obs_data_get_string(data_ptr.get_ptr(), key.as_ptr().0)
41                };
42
43                if result.is_null() {
44                    Err(ObsError::NullPointer(None))
45                } else {
46                    let result = unsafe {
47                        // Safety: The pointer is valid because OBS returned it and we are still in runtime.
48                        CStr::from_ptr(result)
49                    };
50                    let result = result
51                        .to_str()
52                        .map_err(|_| ObsError::StringConversionError)?
53                        .to_string();
54
55                    Ok(Some(result))
56                }
57            } else {
58                Ok(None)
59            }
60        })?
61    }
62    fn get_int<T: Into<ObsString> + Sync + Send>(&self, key: T) -> Result<Option<i64>, ObsError> {
63        let key = key.into();
64        let data_ptr = self.as_ptr();
65
66        run_with_obs!(self.runtime(), (data_ptr, key), move || {
67            let has_value = unsafe {
68                // Safety: We are running on the OBS runtime.
69                has_value(data_ptr.clone(), &key)
70            };
71
72            if has_value {
73                Some(unsafe {
74                    // Safety: The pointer is valid because we are using a smart pointer
75                    libobs::obs_data_get_int(data_ptr.get_ptr(), key.as_ptr().0)
76                })
77            } else {
78                None
79            }
80        })
81    }
82    fn get_bool<T: Into<ObsString> + Sync + Send>(&self, key: T) -> Result<Option<bool>, ObsError> {
83        let key = key.into();
84
85        let data_ptr = self.as_ptr();
86
87        run_with_obs!(self.runtime(), (data_ptr, key), move || {
88            let has_value = unsafe {
89                // Safety: We are running on the OBS runtime.
90                has_value(data_ptr.clone(), &key)
91            };
92
93            if has_value {
94                Some(unsafe {
95                    // Safety: The pointer is valid because we are using a smart pointer
96                    libobs::obs_data_get_bool(data_ptr.get_ptr(), key.as_ptr().0)
97                })
98            } else {
99                None
100            }
101        })
102    }
103    fn get_double<T: Into<ObsString> + Sync + Send>(
104        &self,
105        key: T,
106    ) -> Result<Option<f64>, ObsError> {
107        let key = key.into();
108        let data_ptr = self.as_ptr();
109
110        let result = run_with_obs!(self.runtime(), (key, data_ptr), move || {
111            let has_value = unsafe {
112                // Safety: We are running on the OBS runtime.
113                has_value(data_ptr.clone(), &key)
114            };
115
116            if has_value {
117                Some(unsafe {
118                    // Safety: The pointer is valid because we are using a smart pointer
119                    libobs::obs_data_get_double(data_ptr.get_ptr(), key.as_ptr().0)
120                })
121            } else {
122                None
123            }
124        })?;
125
126        Ok(result)
127    }
128
129    fn get_json(&self) -> Result<String, ObsError> {
130        let data_ptr = self.as_ptr();
131        run_with_obs!(self.runtime(), (data_ptr), move || {
132            let json_ptr = unsafe {
133                // Safety: The pointer is valid because we are using a smart pointer
134                libobs::obs_data_get_json(data_ptr.get_ptr())
135            };
136
137            if json_ptr.is_null() {
138                return Err(ObsError::NullPointer(Some(
139                    "Couldn't get json representation of OBS data".into(),
140                )));
141            }
142
143            let json = unsafe {
144                // Safety: The pointer is valid because OBS returned it and we are still in runtime.
145                CStr::from_ptr(json_ptr)
146            }
147            .to_str()
148            .map_err(|_| ObsError::JsonParseError)?
149            .to_string();
150
151            Ok(json)
152        })?
153    }
154}